package edu.gmu.atelier;

/**
 * Set of vectors with vector combination and scalar multiplication operations.
 * @author James H. Pope
 */
public final class RealElement implements Element
{
    /**
     * Multiplicative inverse requires "1" definition, c*1=c
     */
    public static final RealElement ONE = new RealElement(1.0);
    
    /**
     * Additive inverse requires "0" definition, c+(-c)=0, c+0=0
     */
    public static final RealElement ZERO = new RealElement(0.0);
    
    
    // Instance variables
    private double r = 0.0;
    
    public RealElement(double r)
    {
        this.r = r;
    }
    
    public RealElement(RealElement r)
    {
        this.r = r.r;
    }
    //------------------------------------------------------------------------//
    // Mutator methods, for convenience and improved performance
    //------------------------------------------------------------------------//
    /**
     * Multiplies this field by another field, this field is modified.
     * @param b
     * @return this = this*b
     */
    public void multTo(Element b)
    {
        this.r *= ((RealElement)b).r;
    }
    
    /**
     * Adds this field by another field, this field is modified.
     * @param b
     * @return this = this+b
     */
    public void addTo(Element b)
    {
        this.r += ((RealElement)b).r;
    }
    
    /**
     * Multiplies two specified parameters and adds result to this field.
     * @param x
     * @param v
     * @return this = this+x*v
     */
    public void multAddTo(Element x, Element v)
    {
        this.r = this.r + ((RealElement)v).r * ((RealElement)x).r;
    }
    
    //------------------------------------------------------------------------//
    // Non-mutator methods, immutable and analogous to mathematical functions
    //------------------------------------------------------------------------//
    /**
     * Multiplies this field by another field to produce a new field.
     * @param b
     * @return this*b
     */
    public RealElement mult(Element b)
    {
        return new RealElement( this.r * ((RealElement)b).r );
    }
    
    /**
     * Adds this field by another field to produce a new field.
     * @param b
     * @return this+b
     */
    public RealElement add(Element b)
    {
        return new RealElement( this.r + ((RealElement)b).r );
    }
    
    /**
     * Subtracts b from this element to produce a new element. 
     * @param b
     * @return this-b = this+b.negate()
     */
    public RealElement sub(Element b)
    {
        return new RealElement( this.r - ((RealElement)b).r );
    }
    
    /**
     * Returns a new copy of this object each time called.
     * @return new this
     */
    public RealElement copy()
    {
        return new RealElement(this.r);
    }
    
    /**
     * Returns multiplicative inverse of the element.
     * @return this^(-1) such that this * this^(-1) = identity element
     */
    public RealElement inverse()
    {
        // Ideally true multiplicative identity, but may have round-off
        return new RealElement( 1.0 / this.r );
    }
    
    /**
     * Returns this number raised to the specified power.
     * @return this^exp
     */
    public RealElement power(double exp)
    {
        return new RealElement( Math.pow(r, exp) );
    }
    
    /**
     * Returns square root of the element.
     * @return r such that r*r = this
     */
    public RealElement sqrt()
    {
        return new RealElement( Math.sqrt(r) );
    }
    
    /**
     * Returns absolute value of the element.
     * @return
     */
    public RealElement abs()
    {
        return new RealElement( Math.abs(r) );
    }
    
    /**
     * Returns whether or not element is zero element (e+0=e, e*0=0).
     * @return true if this is zero element
     */
    public boolean isZero()
    {
        return r == +0.0 || r == -0.0;
    }
    
    /**
     * Returns whether or not element (real part) is less than zero.
     * @return true if this is a negative number
     */
    public boolean isNegative()
    {
        return ( this.r < 0.0 );
    }
    
    /**
     * Returns negation of this element.
     * @return -this such that this + (-this) = zero element
     */
    public RealElement negate()
    {
        return new RealElement( -this.r );
    }
    
    /**
     * Returns closest element interpretation to a real scalar.
     * @return 
     */
    public double doubleValue()
    {
        return this.r;
    }
    
    //------------------------------------------------------------------------//
    // Class instance methods
    //------------------------------------------------------------------------//
    public boolean similar( Object obj, Epsilon e )
    {
        if(obj == this)
        {
            return true;
        }
        
        if( (obj instanceof RealElement) == false )
        {
            return false;
        }
        
        RealElement o = (RealElement)obj;
        
        if( e != null )
        {
            if( e.similar(this.r, o.r) == false )
            {
                return false;
            }
        }
        else
        {
            if( this.r != o.r )
            {
                return false;
            }
        }
        
        return true;
    }
    
    public boolean equals( Object obj )
    {
        return this.similar(obj, null);
    }
    
    public int compareTo(Object obj)
    {
        RealElement o = (RealElement)obj;
        if(      this.r > o.r ) return +1;
        else if( this.r < o.r ) return -1;
        return 0;
    }
    
    public String toString()
    {
        return ""+this.r;
    }
    
    
    //------------------------------------------------------------------------//
    // Static accessor methods, does not affect instance of class
    //------------------------------------------------------------------------//
    public Element getZero()
    {
        return ZERO;
    }
    
    public Element getOne()
    {
        return ONE;
    }
}
